#include "headers/LevelMenager.h"

#include "headers/PPUSystem.h"
#include "headers/FileLoader.h"
#include "headers/Enemies.h"
#include "headers/Fonts.h"
#include "headers/Etc.h"

#include <fstream>
#include <sstream>

extern FileLoader cFileLoader;
extern PPUSystem cPPUSystem;
extern WindowsMenager cWindowsMenager;
extern SquaresList cSquaresList;
extern Enemies cEnemies;

   
inline unsigned int ReadWord(int addr) {
    unsigned int value = cFileLoader.Read(addr+1);
    value<<=8;
    value|=cFileLoader.Read(addr);
    value&=0xFFFF;
    return value;
}

int LevelMenager::LoadLevel(int id) {
          
    LvlCur = id;
    cWindowsMenager.Clear(); //wyczysc ekran
    
    //wczytaj wszystkie dane o levelu:
        
    cFileLoader.SetBank(1); //wszystkie dane sa w pierwszym banku
    int address = ReadWord(0x8070 + LvlCur*2);
    //0x8070 - tabela z adresami tabel informacji o levelach
    //address - adres tablicy z info o levelu
    
    Info.SqrX = cFileLoader.Read(address);
    Info.SqrsIDsAddress = ReadWord(address+20);
    Info.SqrsDataAddress = ReadWord(address+3);
    Info.SqrsDataBank = cFileLoader.Read(address+9);
    
    Info.SqrY = 6;
    Info.SqrsIDsBank = 2;
    Info.pal = 0;
    Info.ID = LvlCur;
    
    unsigned int PalAddr = ReadWord(address+12); //adres palety     
    unsigned int CopyInfoAddr = ReadWord(address+14); //adres tabeli z informacjami o patternach do wczytania
    unsigned int TextAddr = ReadWord(address+16); //adres opisu
    
    //******************
    //znajdz EnemyAdress
    //******************   
    for (Info.EnemyAdress = 0x86E0; Info.EnemyAdress<0xFFFF; Info.EnemyAdress+=3)
        if (cFileLoader.Read(Info.EnemyAdress)==Info.ID)
            break;
    
    //**********************
    //Wyswietl info o levelu
    //**********************
/*
    std::stringstream text;
    const int h = 16;
    
    SetFont("Times New Roman Bold.ttf", 12);
    
    text << "LvlCur: " << std::hex << LvlCur;
    OutText(text.str().c_str(), 420, 10);
    text.str("");
    text << "Adres tabeli: " << std::hex << address;
    OutText(text.str().c_str(), 420, 10+h);
    text.str("");
    text << "X: " << std::hex << Info.SqrX;
    OutText(text.str().c_str(), 420, 10+h*2);
    text.str("");
    text << "CopyInfoAddr: " << std::hex << CopyInfoAddr;
    OutText(text.str().c_str(), 420, 10+h*3);
    text.str("");    
    text << "SqrsDataAddress: " << std::hex << Info.SqrsDataAddress;
    OutText(text.str().c_str(), 420, 10+h*4);
    text.str("");
    text << "EnemyAdress: " << std::hex << Info.EnemyAdress;
    OutText(text.str().c_str(), 420, 10+h*5);
    text.str("");
*/
        
    //************
    //wczytaj opis
    //************
    TextAddr+=2; //nie wiem co robia te dwa bajty
    int lines = 0;
    
    SetFont("Balcony Angels.ttf", 18);
    
    while(1) {
 
        std::stringstream text;    
        unsigned char t;
        
        for(int i=0;;i++) {
            t = cFileLoader.Read(TextAddr); //wczytaj znak
            TextAddr++;
            
            if (t==0xFE || t==0xFD || t==0xFF) break;
            //0xFE - nowa linia
            
            if (t>='A' && t<='z')
                switch (t) {
                    case 'd':
                        text<<"D"; break;
                    case 'm': case 'w':
                        break;
                        text<<" "; break;
                    default:
                        text<<(char)t;
                }
            if (t==',' || t=='\'') text << (char)t;
            if (t==0x20) text<<" ";
            
            //wiecej liter niz powinno byc
            if (i>50) {
                SomethingWrong();
                return 1;
            }
             
        }
        
        OutText(text.str().c_str(), 420, 20+lines*16);
        text.str("");
        
        if (t==0xFD || t==0xFF) break;
        lines++; //nastepna linia
        
        if (lines>8) {
            SomethingWrong();
            return 1;
        }
    }
    
    //**************
    //wczytaj palete
    //**************  
    for (int i=0, j=0; i<0x10; i++)
        if (i%4==0) {
            j++; 
            cPPUSystem.SetColorInPalette(i, cFileLoader.Read(PalAddr+3));
        }
        else
            cPPUSystem.SetColorInPalette(i, cFileLoader.Read(PalAddr+4+i-j));  
    
    char pal[] = { 
        0x00,0x0d,0x10,0x20,
        0x00,0x0d,0x15,0x26,
        0x00,0x06,0x16,0x26,
        0x00,0x10,0x3c,0x20
    };
    
    for (int i=0; i<0x10; i++)
        cPPUSystem.SetColorInPalette(i+0x10, pal[i]);
    
    
    //**************** 
    //skopiuj patterny
    //****************    
    struct {
        unsigned char Bank; 
        unsigned int Adress;
        unsigned char StartPattern;
        unsigned char HowMany;
    }CopyInfo; //info o kopiowaniu patternow 
            
    for (int i=0;;i++) {
            
        //wczytaj numer banku, z ktorego patterny beda kopiowane
        CopyInfo.Bank = cFileLoader.Read(CopyInfoAddr);
        
        if (CopyInfo.Bank==0xff) { //nie wiem, co dokladnie robi to 0xFF
            //CopyInfoAddr++;
            //continue;
            break;
        } else             
        if (CopyInfo.Bank==0) //wczytano wszystkie patterny
            break;
        
        //wczytaj reszte danych
        CopyInfo.HowMany = cFileLoader.Read(CopyInfoAddr+1);
        CopyInfo.StartPattern = cFileLoader.Read(CopyInfoAddr+2);
        CopyInfo.Adress = ReadWord(CopyInfoAddr+3);
        
        //wczytaj patterny
        int LastBank = cFileLoader.CurrentBank(); 
        cFileLoader.SetBank(CopyInfo.Bank);
        cPPUSystem.Uncompress(BACKGROUND, CopyInfo.Adress, CopyInfo.StartPattern, CopyInfo.HowMany); 
        cFileLoader.SetBank(LastBank);
        
        CopyInfoAddr+=5;
        
        if (i>50) {
            SomethingWrong();
            return 1;
        }
    }
    
    //wczytaj sprajty obiektow
    cFileLoader.SetBank(7);
    cPPUSystem.Uncompress(SPRITES, 0x9670, 0, 4); //pajak
    cPPUSystem.Uncompress(SPRITES, 0xAD1E, 4, 4); //ptak
    cPPUSystem.Uncompress(SPRITES, 0xA745, 8, 2); //mrowka
    cPPUSystem.Uncompress(SPRITES, 0x976E, 10, 1); //kropla
    cPPUSystem.Uncompress(SPRITES, 0x9EF6, 11, 4); //japko
    cPPUSystem.Uncompress(SPRITES, 0x9F34, 15, 4); //wisnia
    cFileLoader.SetBank(6);
    cPPUSystem.Uncompress(SPRITES, 0xBF91, 19, 4); //krysztal
    
    //rysuj squares
    cSquaresList.DrawSquares("SquaresList");
    
    cEnemies.Reset(); //zaden wrog nie jest zaznaczony
    
    //poustawiaj okno
    cWindowsMenager.SetSrcX(WindowName.c_str(), 0);
    cWindowsMenager.SetDestW(WindowName.c_str(), 380);
    
    /* If surface is too small or too large - resize it */
    if (cWindowsMenager.GetW(WindowName.c_str()) != GetPixelsX()) {
        cWindowsMenager.Resize(WindowName.c_str(), GetPixelsX(), 192);
    }
    if (GetPixelsX()<380)
        cWindowsMenager.Clear(WindowName.c_str());
        
    //rysuj
    DrawLevel(WindowName.c_str(), 0, 0);
    
}

int LevelMenager::DrawLevel(const char* WinName, int x, int y) {
       
    /* Draw all squares */
    for (int i=0; i<GetLvlInfo()->SqrY; i++) { //y
        for(int j=0; j<GetLvlInfo()->SqrX; j++) { //x
        
            cFileLoader.SetBank(GetLvlInfo()->SqrsIDsBank);
        
            int id = cFileLoader.Read(GetLvlInfo()->SqrsIDsAddress+j+i*GetLvlInfo()->SqrX);          
            
            cFileLoader.SetBank(GetLvlInfo()->SqrsDataBank);
            DrawSquare(WinName, id, x+j*32, y+i*32);
        }
    }

}

int LevelMenager::DrawCurrentSquare(const char* WinName, int x, int y) {
    
    /* Draw actual square: */
    /* Get ID of square */
    cFileLoader.SetBank(GetLvlInfo()->SqrsIDsBank);
    int id = cFileLoader.Read(GetLvlInfo()->SqrsIDsAddress+SqrCur.x+SqrCur.y*GetLvlInfo()->SqrX);
    
    /* Draw square */
    cFileLoader.SetBank(GetLvlInfo()->SqrsDataBank);
    DrawSquare(WinName, id, x, y);
    
}

int LevelMenager::DrawFrame() {

    cWindowsMenager.DrawFrame(WindowName.c_str(), GetRGB(0xff,0xff,00), SqrCur.x*32, SqrCur.y*32, 32, 32);    

}

int LevelMenager::MouseClick(int x, int y, int b) {
    
    x /= 32;
    y /= 32;
    
    if (x<GetLvlInfo()->SqrX && y<GetLvlInfo()->SqrY) {
        SqrCur.x = x;
        SqrCur.y = y;
        cSquaresList.SetCurrentSquare( GetCurrentSquareID() );
    }
}

unsigned char LevelMenager::GetCurrentSquareID() {
    cFileLoader.SetBank(GetLvlInfo()->SqrsIDsBank);
    return cFileLoader.Read(GetLvlInfo()->SqrsIDsAddress+SqrCur.x+SqrCur.y*GetLvlInfo()->SqrX);
}

void LevelMenager::SetCurrentSquareID(unsigned char id) {
    cFileLoader.SetBank(GetLvlInfo()->SqrsIDsBank);
    cFileLoader.Write(GetLvlInfo()->SqrsIDsAddress+SqrCur.x+SqrCur.y*GetLvlInfo()->SqrX, id);
    DrawCurrentSquare(WindowName.c_str(), SqrCur.x*32, SqrCur.y*32);
}

int LevelMenager::SetDrawObjectName(const char* name) {
    WindowName = name;
}

int LevelMenager::DrawSquare(const char* WinName, int id, int x, int y) {
    
    int atr;
    
    //calculate address of square:
    int address = id;
    int temp = address;
    address <<= 4;
    address += temp;
    address += GetLvlInfo()->SqrsDataAddress;
    
       
    cFileLoader.SetBank(GetLvlInfo()->SqrsDataBank);
    
    //draw square:
        
    cWindowsMenager.StartDrawingBackground(WinName);
        
    for (int i=0; i<4; i++) //y
        for (int j=0; j<4; j++) { //x

            cPPUSystem.DrawPattern( BACKGROUND, 
                                   cFileLoader.Read(address+i*4+j),
                                   cFileLoader.Read(address+16)>>((j/2 + (i/2)*2)*2)&3,
                                   WinName,
                                   j*8+x,
                                   i*8+y );
        }
    
    cWindowsMenager.StopDrawingBackground(WinName);

}

const LevelInfo* LevelMenager::GetLvlInfo() {
    return &Info; //info o wczytanym levelu
}

            
        
    
    
    
    
    
    
